{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Modeling Transmission Line Properties\n",
"## Table of Contents\n",
"* [Introduction](#introduction)\n",
" * [Propagation constant](#propagation_constant)\n",
" * [Interlude on attenuation units](#attenuation_units)\n",
"* [Modeling a loaded lossy transmission line using transmission line functions](#tline_functions)\n",
" * [Input impedances, reflection coefficients and SWR](#tline_impedances)\n",
" * [Voltages and Currents](#voltages_currents)\n",
"* [Modeling a loaded lossy transmission line by cascading Networks](#cascading_networks)\n",
"* [Determination of the propagation constant from the input impedance](#propagation_constant_from_zin)\n",
"\n",
"\n",
"## Introduction \n",
"In this tutorial, `scikit-rf` is used to work with some classical transmission line situations, such as calculating impedances, reflection coefficients, standing wave ratios or voltages and currents. There is at least two way of performing these calculations, one using [transmission line functions](#tline_functions) or by [creating and cascading Networks](#cascading_networks)\n",
"\n",
"Let's consider a lossy coaxial cable of characteristic impedance $Z_0=75 \\Omega$ of length $d=12 m$. The coaxial cable has an attenuation of 0.02 Neper/m and a [velocity factor](https://en.wikipedia.org/wiki/Velocity_factor) VF=0.67 (This corresponds roughly to a [RG-6](https://en.wikipedia.org/wiki/RG-6) coaxial). The cable is loaded with a $Z_L=150 \\Omega$ impedance. The RF frequency of interest is 250 MHz. \n",
"\n",
"Please note that in `scikit-rf`, the line length is defined from the load, ie $z=0$ at the load and $z=d$ at the input of the transmission line:\n",
"
\n",
"\n",
"\n",
"First, let's make the necessary Python import statements:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%matplotlib inline\n",
"import matplotlib.pyplot as plt\n",
"import numpy as np\n",
"\n",
"import skrf as rf\n",
"from skrf.constants import c\n",
"from skrf.mathFunctions import db_2_np, db_per_100feet_2_db_per_100meter, feet_2_meter, mag_2_db10, np_2_db\n",
"from skrf.tlineFunctions import (\n",
" reflection_coefficient_2_propagation_constant,\n",
" voltage_current_propagation,\n",
" zl_2_Gamma0,\n",
" zl_2_Gamma_in,\n",
" zl_2_swr,\n",
" zl_2_total_loss,\n",
" zl_2_zin,\n",
")\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# skrf figure styling\n",
"rf.stylely()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"And the constants of the problem:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"freq = rf.Frequency(250, npoints=1, unit='MHz')\n",
"Z_0 = 75 # Ohm\n",
"Z_L = 150 # Ohm\n",
"d = 12 # m\n",
"VF = 0.67\n",
"att = 0.02 # Np/m. Equivalent to 0.1737 dB/m"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Before going into impedance and reflection coefficient calculations, first we need to define the transmission line properties, in particular its propagation constant."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Propagation constant \n",
"In order to get the RF parameters of the transmission line, it is necessary to derive the propagation constant of the line. The propagation constant $\\gamma$ of the line is defined in `scikit-rf` as $\\gamma=\\alpha + j\\beta$ where $\\alpha$ is the attenuation (in Neper/m) and $\\beta=\\frac{2\\pi}{\\lambda}=\\frac{\\omega}{c}/\\mathrm{VF}=\\frac{\\omega}{c}\\sqrt{\\epsilon_r}$ the phase constant.\n",
"\n",
"First, the wavelength in the coaxial cable is $$\\lambda=\\frac{c}{f \\sqrt{\\epsilon_r}}=\\frac{c}{f} \\mathrm{VF} $$ "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"lambd = c/freq.f * VF\n",
"print('VF=', VF, 'and Wavelength:', lambd, 'm')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As the attenuation is already given in Np/m, the propagation constant is:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"alpha = att # Np/m !\n",
"beta = freq.w/c/VF\n",
"gamma = alpha + 1j*beta\n",
"print('Transmission line propagation constant: gamma = ', gamma, 'rad/m')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If the attenuation would have been given in other units, `scikit-rf` provides the necessary tools to convert units, as described below."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Interlude: On Attenuation Units "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Attenuation is generally provided (or expected) in various kind of units. `scikit-rf` provides convenience functions to manipulate line attenuation units. \n",
"\n",
"For example, the cable attenuation given in Np/m, can be expressed in dB:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print('Attenuation dB/m:', np_2_db(att))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Hence, the attenuation in dB/100m is:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print('Line attenuation in dB/100m:', np_2_db(att)*100)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"And in dB/100feet is:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print('Line attenuation in dB/100ft:', np_2_db(att)*100*feet_2_meter())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If the attenuation would have been given in imperial units, such as dB/100ft, the opposite conversions would have been: "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"db_per_100feet_2_db_per_100meter(5.2949) # to dB/100m"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"db_2_np(5.2949)/feet_2_meter(100) # to Np/m"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Using transmission line functions \n",
"`scikit-rf` brings few convenient functions to deal with transmission lines. They are detailed in the [transmission line functions](../../api/tlineFunctions.rst) documentation pages. \n",
"\n",
"### Input impedances, reflection coefficients and SWR \n",
"The reflection coefficient $\\Gamma_L$ induced by the load is given by `zl_2_Gamma0()`:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"Gamma0 = zl_2_Gamma0(Z_0, Z_L)\n",
"print('|Gamma0|=', abs(Gamma0))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"and its associated Standing Wave Ratio (SWR) is obtained from `zl_2_swr()`:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"zl_2_swr(Z_0, Z_L)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"After propagating by a distance $d$ in the transmission line of propagation constant $\\gamma$ (hence having travelled an electrical length $\\theta=\\gamma d$), the reflection coefficient at the line input is obtained from `zl_2_Gamma_in()`:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"Gamma_in = zl_2_Gamma_in(Z_0, Z_L, theta=gamma*d)\n",
"print('|Gamma_in|=', abs(Gamma_in), 'phase=', 180/np.pi*np.angle(Gamma_in))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The input impedance $Z_{in}$ from `zl_2_zin()`:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"Z_in = zl_2_zin(Z_0, Z_L, gamma * d)\n",
"print('Input impedance Z_in=', Z_in)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Like previously, the SWR at the line input is:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"zl_2_swr(Z_0, Z_in)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The total line loss in dB is get from `zl_2_total_loss()`:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"mag_2_db10(zl_2_total_loss(Z_0, Z_L, gamma*d))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Voltages and Currents "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now assume that the previous circuit is excited by a source delivering a voltage $V=1 V$ associated to a source impedance $Z_s=100\\Omega$ :\n",
"
"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"Z_s = 100 # Ohm\n",
"V_s = 1 # V"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"At the input of the transmission line, the voltage is a voltage divider circuit:\n",
"$$\n",
"V_{in} = V_s \\frac{Z_{in}}{Z_s + Z_{in}}\n",
"$$"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"V_in = V_s * Z_in / (Z_s + Z_in)\n",
"print('Voltage at transmission line input : V_in = ', V_in, ' V')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"and the current at the input of the transmission line is:\n",
"$$\n",
"I_{in} = \\frac{V_s}{Z_s + Z_{in}}\n",
"$$"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"I_in = V_s / (Z_s + Z_in)\n",
"print('Current at transmission line input : I_in = ', I_in, ' A')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"which represent a power of \n",
"$$\n",
"P_{in} = \\frac{1}{2} \\Re \\left[V_{in} I_{in}^* \\right]\n",
"$$"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"P_in = 1/2 * np.real(V_in * np.conj(I_in))\n",
"print('Input Power : P_in = ', P_in, 'W')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The reflected power is:\n",
"$$\n",
"P_r = |\\Gamma_{in}|^2 P_{in}\n",
"$$"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"P_r = abs(Gamma_in)**2 * P_in\n",
"print('Reflected power : P_r = ', P_r, 'W')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The voltage and current at the load can be deduced from the ABCD parameters of the line of length $L$ :"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"V_out, I_out = voltage_current_propagation(V_in, I_in, Z_0,theta= gamma*d)\n",
"print('Voltage at load: V_out = ', V_out, 'V')\n",
"print('Current at load: I_out = ', I_out, 'A')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Note that voltages and currents are expressed a peak values. RMS values are thus:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(abs(V_out)/np.sqrt(2), abs(I_out)/np.sqrt(2))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The power delivered to the load is thus:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"P_out = 1/2 * np.real(V_out * np.conj(I_out))\n",
"print('Power delivered to the load : P_out = ', P_out, ' W')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Voltage and current are plotted below against the transmission line length (pay attention to the sign of $d$ in the voltage and current propagation: as we go from source ($z=d$) to the load ($z=0$), $\\theta$ goes in the opposite direction and should be inversed)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ds = np.linspace(0, d, num=1001)\n",
"\n",
"thetas = - gamma*ds\n",
"\n",
"v1 = np.full_like(ds, V_in)\n",
"i1 = np.full_like(ds, I_in)\n",
"\n",
"v2, i2 = voltage_current_propagation(v1, i1, Z_0, thetas)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig, (ax_V, ax_I) = plt.subplots(2, 1, sharex=True)\n",
"ax_V.plot(ds, abs(v2), lw=2)\n",
"ax_I.plot(ds, abs(i2), lw=2, c='C1')\n",
"ax_I.set_xlabel('z [m]')\n",
"ax_V.set_ylabel('|V| [V]')\n",
"ax_I.set_ylabel('|I| [A]')\n",
"\n",
"\n",
"ax_V.axvline(0, c='k', lw=5)\n",
"ax_I.axvline(0, c='k', lw=5)\n",
"ax_V.text(d-2, 0.4, 'input')\n",
"ax_V.text(1, 0.6, 'load')\n",
"ax_V.axvline(d, c='k', lw=5)\n",
"ax_I.axvline(d, c='k', lw=5)\n",
"\n",
"ax_I.set_title('Current')\n",
"ax_V.set_title('Voltage')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Using `media` objects for transmission line calculations "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`scikit-rf` also provides objects representing transmission line mediums. The `Media` object provides generic methods to produce Network’s for any transmission line medium, such as transmission line length (`line()`), lumped components (`resistor()`, `capacitor()`, `inductor()`, `shunt()`, etc.) or terminations (`open()`, `short()`, `load()`). For additional references, please see the [media documentation](../../api/media/index.rst). \n",
"\n",
"Let's create a transmission line `media` object for our coaxial line of characteristic impedance $Z_0$ and propagation constant $\\gamma$:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# if not passing the gamma parameter, it would assume that gamma = alpha + j*beta = 0 + j*1\n",
"coax_line = rf.media.DefinedGammaZ0(frequency=freq, z0=Z_0, gamma=gamma)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In order to build the circuit illustrated by the figure above, all the circuit's Networks are created and then [cascaded](../../tutorials/Networks.rst#Cascading-and-De-embedding) with the `**` operator: \n",
"\n",
"
\n",
"\n",
" * [transmission line](../../api/media/generated/skrf.media.Media.line.rst) of length $d$ (from the media created above), \n",
" * a [resistor](../../api/media/generated/skrf.media.Media.resistor.rst) of impedance $Z_L$, \n",
" * then terminated by a [short](../../api/media/generated/skrf.media.Media.short.rst). \n",
"\n",
"This results in a one-port network, which $Z$-parameter is then the input impedance: "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ntw = coax_line.line(d, unit='m') ** coax_line.resistor(Z_L) ** coax_line.short()\n",
"ntw.z"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Note that full Network can also be built with convenience functions [load](../../api/media/generated/skrf.media.Media.load.rst):"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ntw = coax_line.line(d, unit='m') ** coax_line.load(zl_2_Gamma0(Z_0, Z_L))\n",
"ntw.z"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"or even more directly using or [delay_load](../../api/media/generated/skrf.media.Media.delay_load.rst):"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ntw = coax_line.delay_load(zl_2_Gamma0(Z_0, Z_L), d, unit='m')\n",
"ntw.z"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Determination of the propagation constant from the input impedance \n",
"Let's assume the input impedance of a short‐circuited lossy transmission line of length d=1.5 m and a characteristic impedance of $Z_0=$100 Ohm has been measured to $Z_{in}=40 - 280j \\Omega$. \n",
"\n",
"
\n",
"\n",
"The transmission line propagation constant $\\gamma$ is unknown and researched. Let see how to deduce its value using `scikit-rf`:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# input data\n",
"z_in = 20 - 140j\n",
"z_0 = 75\n",
"d = 1.5\n",
"Gamma_load = -1 # short"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Since we know the input impedance, we can deduce the reflection coefficient at the input of the transmission line. Since there is a direction relationship between the reflection coefficient at the load and at the input of the line:\n",
"\n",
"$$\n",
"\\Gamma_{in} = \\Gamma_L e^{- 2 \\gamma d}\n",
"$$\n",
"\n",
"we can deduce the propagation constant value $\\gamma$ as:\n",
"$$\n",
"\\gamma = -\\frac{1}{2d} \\ln \\left( \\frac{\\Gamma_{in}}{\\Gamma_l} \\right)\n",
"$$\n",
"\n",
"This is what the convenience function `reflection_coefficient_2_propagation_constant` is doing:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# reflection coefficient at input\n",
"Gamma_in = zl_2_Gamma0(z_0, z_in)\n",
"# line propagation constant\n",
"gamma = reflection_coefficient_2_propagation_constant(Gamma_in, Gamma_load, d)\n",
"print('Line propagation constant, gamma =', gamma, 'rad/m')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"One can check the consistency of the result by making the reverse calculation: the input impedance at a distance $d$ from the load $Z_l$:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"zl_2_zin(z_0, zl=0, theta=gamma * d)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Which was indeed the value given as input of the example."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now that the line propagation constant has been determined, one can replace the short by a load resistance:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"zl_2_zin(z_0, zl=50+50j, theta=gamma * d)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.10"
}
},
"nbformat": 4,
"nbformat_minor": 2
}